package com.example.aigc_education.service.impl;

//import com.agentsflex.llm.MessageResponse;
import com.agentsflex.llm.spark.SparkLlm;
//import com.agentsflex.message.AiMessage;
//import com.agentsflex.message.HumanMessage;
//import com.agentsflex.message.SystemMessage;
//import com.agentsflex.prompt.HistoriesPrompt;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.TableFieldInfo;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.example.aigc_education.common.BusinessException;
import com.example.aigc_education.constant.CacheConstants;
import com.example.aigc_education.constant.WeChatConstants;
import com.example.aigc_education.domain.User;
import com.example.aigc_education.domain.dto.ModifyByAdminDTO;
import com.example.aigc_education.domain.dto.ModifyByUserDTO;
import com.example.aigc_education.domain.dto.SelectByConditionDTO;
import com.example.aigc_education.domain.vo.HaveSignVO;
import com.example.aigc_education.domain.vo.SignRewardVO;
import com.example.aigc_education.exception.ServiceException;
import com.example.aigc_education.exception.UserPasswordNotMatchException;
import com.example.aigc_education.mapper.CourseMapper;
import com.example.aigc_education.security.domain.LoginUser;
import com.example.aigc_education.security.service.TokenService;
import com.example.aigc_education.security.token.MobileAuthenticationToken;
import com.example.aigc_education.service.UserService;
import com.example.aigc_education.mapper.UserMapper;
import com.example.aigc_education.utils.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.sql.Timestamp;
import java.text.SimpleDateFormat;
import java.time.LocalDateTime;
import java.time.YearMonth;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.function.Predicate;


/**
 * @author 23918
 * @description 针对表【sys_user】的数据库操作Service实现
 * @createDate 2024-05-22 20:25:46
 */
@Slf4j
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User>
        implements UserService {
    @Resource
    private AuthenticationManager authenticationManager;

    @Resource
    private RedisCache redisCache;

    @Resource
    private TokenService tokenService;

    @Resource
    private LoginUtils loginUtils;

    @Resource
    private SmsUtils smsUtils;

    @Resource
    private UserMapper userMapper;

    @Resource
    private CourseMapper courseMapper;

    @Resource
    private SparkLlm sparkLlm;

    @Autowired
    private WeChatConstants weChatConstants;


    /**
     * 用户名密码登录接口
     *
     * @param username 用户名
     * @param password 用户密码
     * @param code     验证码
     * @param uuid     验证码UUID，用于验证验证码的有效性
     * @return 返回登录成功后的令牌（Token）
     */
    @Override
    public String loginByUsername(String username, String password, String code, String uuid) {
        // 验证验证码的正确性
        loginUtils.validateCaptcha(username, code, uuid);
        // 对手机号和密码进行前置校验
        loginUtils.loginPreCheck(username, password);

        // 用户认证过程
        Authentication authentication = null;
        try {
            // 构建认证令牌
            UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(username, password);
            // 设置认证令牌到线程本地变量
            ThreadLocalUtil.setContext(authenticationToken);
            // 执行认证
            authentication = authenticationManager.authenticate(authenticationToken);
        } catch (Exception e) {
            // 处理认证异常
            if (e instanceof BadCredentialsException) {
                // 密码匹配异常
                throw new UserPasswordNotMatchException();
            } else {
                // 其他异常
                throw new ServiceException(e.getMessage());
            }
        } finally {
            // 清理线程本地变量
            ThreadLocalUtil.clearContext();
        }

        // 获取登录用户信息，并构造令牌所需的声明
        LoginUser loginUser = (LoginUser) authentication.getPrincipal();

        // 生成并返回令牌
        return tokenService.createToken(loginUser);
    }


    /**
     * 通过手机号和验证码进行登录。
     *
     * @param phone 用户的手机号。
     * @param code  用户收到的短信验证码。
     * @return 登录成功后返回的令牌。
     * @throws RuntimeException              当验证码为空或者错误时抛出。
     * @throws UserPasswordNotMatchException 当手机号和密码不匹配时抛出。
     * @throws ServiceException              当认证过程中发生其他异常时抛出。
     */
    @Override
    public String loginByPhone(String phone, String code) {
        if (code == null) {
            throw new RuntimeException("验证码不能为空");
        }

        // 校验短信验证码是否正确
        boolean checkResult = smsUtils.check(phone, code);
        if (!checkResult) {
            throw new BusinessException("验证码错误");
        } else {
            // 验证码正确后删除验证码
            smsUtils.delete(phone);
        }

        Authentication authentication = null;
        try {
            // 构建手机认证令牌
            MobileAuthenticationToken mobileAuthenticationToken = new MobileAuthenticationToken(phone);
            // 将认证令牌设置到线程本地上下文中
            ThreadLocalUtil.setContext(mobileAuthenticationToken);
            // 执行用户认证
            authentication = authenticationManager.authenticate(mobileAuthenticationToken);
        } catch (Exception e) {
            if (e instanceof BadCredentialsException) {
                // 手机号和验证码不匹配异常处理
                throw new UserPasswordNotMatchException();
            } else {
                // 其他异常处理
                throw new ServiceException(e.getMessage());
            }
        } finally {
            // 清理线程本地上下文
            ThreadLocalUtil.clearContext();
        }

        // 获取登录用户信息并生成令牌
        LoginUser loginUser = (LoginUser) authentication.getPrincipal();
        String token = tokenService.createToken(loginUser);
        return token;
    }

    /**
     * 根据用户名获取用户信息。
     *
     * @param username 需要查询的用户名。
     * @return 返回匹配的用户对象，如果没有找到则返回null。
     */
    @Override
    public User getUserByUsername(String username) {
        // 创建查询包装器，并设置查询条件
        QueryWrapper<User> wrapper = new QueryWrapper<>(new User());

        // 筛选查询字段，不包括密码字段
        wrapper.select(new Predicate<TableFieldInfo>() {
            @Override
            public boolean test(TableFieldInfo tableFieldInfo) {
                // 返回不等于密码字段的条件
                return !"password".equals(tableFieldInfo.getColumn());
            }
        });

        // 设置用户名等于传入的username作为查询条件
        wrapper.eq("user_name", username);

        // 执行查询并返回结果
        return userMapper.selectOne(wrapper);

    }

    /**
     * 根据用户ID更新用户信息。
     *
     * @param userId          用户ID，用于指定要更新的用户。
     * @param modifyByUserDto 包含要更新的用户信息的数据传输对象，各字段非必填，可选更新用户姓名、性别、真实姓名、年龄、等级、描述和头像URL。
     * @return 返回一个布尔值，表示更新操作是否成功。成功则返回true，通常情况下该方法不会失败。
     * @throws BusinessException 如果更新过程中发生异常，则抛出业务异常，提示更新用户信息失败。
     */
    @Override
    @Transactional
    public Boolean updateInfoByUserId(Long userId, ModifyByUserDTO modifyByUserDto) {
        Timestamp now = new Timestamp(System.currentTimeMillis());
        SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

        // 创建更新条件包装器，逐步设置更新条件和更新字段
        UpdateWrapper<User> wrapper = new UpdateWrapper<>(new User());
        try {
            // 根据传入的modifyByUserDto动态设置更新字段，如果字段不为空，则加入更新条件
            if (modifyByUserDto.getUserName() != null) {
                wrapper.set("user_name", modifyByUserDto.getUserName());
            }

            if (modifyByUserDto.getGender() != null) {
                wrapper.set("gender", modifyByUserDto.getGender());
            }
            if (modifyByUserDto.getRealName() != null) {
                wrapper.set("real_name", modifyByUserDto.getRealName());
            }
            if (modifyByUserDto.getAge() != null) {
                wrapper.set("age", modifyByUserDto.getAge());
            }
            if (modifyByUserDto.getLevel() != null) {
                wrapper.set("level", modifyByUserDto.getLevel());
            }
            if (modifyByUserDto.getDescription() != null) {
                wrapper.set("description", modifyByUserDto.getDescription());
            }
            if (modifyByUserDto.getAvatarUrl() != null) {
                wrapper.set("avatar_url", modifyByUserDto.getAvatarUrl());
            }
            // 设置更新者名称和更新时间
            wrapper.set("update_by", SecurityUtils.getUserId());
            wrapper.set("update_time", df.format(now));
            // 设置用户ID条件，用于指定更新哪条用户记录
            wrapper.eq("user_id", userId);
            // 执行更新操作
            userMapper.update(null, wrapper);
        } catch (Exception e) {
            // 如果更新过程中出现异常，抛出业务异常
            throw new BusinessException("更新用户信息失败");
        }
        // 更新成功，返回true
        return true;

    }


    /**
     * 添加用户到系统中。
     *
     * @param user 用户对象，包含用户信息。
     * @return 如果用户添加成功返回true，否则返回false。
     */
    @Override
    @Transactional
    public Boolean addUser(User user) {
       try {
           // 如果用户对象为空，设置默认密码
        if (user == null) {
            user.setPassword("123456");
        }
        // 使用BCryptPasswordEncoder对密码进行加密
        BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
        user.setPassword(encoder.encode(user.getPassword()));
        // 设置用户创建信息
        user.setCreateBy(1L);
        user.setCreateTime(LocalDateTime.now());
        // 尝试将用户信息插入数据库
        int resNum = userMapper.insert(user);
        // 根据插入结果返回操作成功与否
        if (resNum == 1) {
            return true;
        } else
            return false;
    }catch (Exception e){
        throw new BusinessException("添加用户失败");
    }
    }
    /**
     * 通过管理员修改用户信息
     *
     * @param modifyByAdminDto 包含需要修改的用户信息的Dto对象。
     * @return 返回一个布尔值，表示是否成功更新用户信息。
     * @throws BusinessException 如果更新过程中出现异常，抛出此异常。
     */
    @Override
    @Transactional
    public Boolean updateInfoByAdmin( ModifyByAdminDTO modifyByAdminDto) {
        // 创建一个更新条件包装器，用于构建更新语句
        // 创建更新条件包装器，逐步设置更新条件和更新字段
        UpdateWrapper<User> wrapper = new UpdateWrapper<>(new User());
        try {
            // 根据modifyByAdminDto的内容，动态设置需要更新的字段
            // 根据传入的modifyByUserDto动态设置更新字段，如果字段不为空，则加入更新条件
            if (modifyByAdminDto.getUserName() != null) {
                wrapper.set("user_name", modifyByAdminDto.getUserName());
            }
            if (modifyByAdminDto.getGender() != null) {
                wrapper.set("gender", modifyByAdminDto.getGender());
            }
            if (modifyByAdminDto.getRealName() != null) {
                wrapper.set("real_name", modifyByAdminDto.getRealName());
            }
            if (modifyByAdminDto.getAge() != null) {
                wrapper.set("age", modifyByAdminDto.getAge());
            }
            if (modifyByAdminDto.getLevel() != null) {
                wrapper.set("level", modifyByAdminDto.getLevel());
            }
            if (modifyByAdminDto.getDescription() != null) {
                wrapper.set("description", modifyByAdminDto.getDescription());
            }
            if (modifyByAdminDto.getAvatarUrl() != null) {
                wrapper.set("avatar_url", modifyByAdminDto.getAvatarUrl());
            }
            if (modifyByAdminDto.getStudyStyle() != null) {
                wrapper.set("study_style", modifyByAdminDto.getStudyStyle());
            }
            if (modifyByAdminDto.getPhone() != null) {
                wrapper.set("phone", modifyByAdminDto.getPhone());
            }
            if (modifyByAdminDto.getAccountId() != null) {
                wrapper.set("account_id", modifyByAdminDto.getAccountId());
            }
            if (modifyByAdminDto.getStudyHabit() != null) {
                wrapper.set("study_habit", modifyByAdminDto.getStudyHabit());
            }
            if (modifyByAdminDto.getStatus() != null) {
                wrapper.set("status", modifyByAdminDto.getStatus());
            }

            // 如果密码不为空，使用BCryptPasswordEncoder对密码进行加密
            if (modifyByAdminDto.getPassword() != null){
                BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
            }
            // 设置更新者名称和更新时间
            // 设置更新者名称和更新时间
            wrapper.set("update_by", SecurityUtils.getUserId());
            wrapper.set("update_time", LocalDateTime.now());
            // 设置用户ID条件，用于指定更新哪条用户记录
            // 设置用户ID条件，用于指定更新哪条用户记录
            wrapper.eq("user_id", modifyByAdminDto.getUserId());
            // 执行更新操作
            // 执行更新操作
            userMapper.update(null, wrapper);
        } catch (Exception e) {
            // 如果更新过程中出现异常，抛出业务异常
            // 如果更新过程中出现异常，抛出业务异常
            throw new BusinessException("更新用户信息失败");
        }
        // 更新成功，返回true
        // 更新成功，返回true
        return true;
    }


    /**
     * 获取所有用户信息的方法。
     *
     * 通过调用userMapper的selectList方法，查询数据库中的所有用户信息。
     * 如果查询过程中发生异常，将抛出一个包含错误信息的BusinessException。
     *
     * @return List<User> 返回包含所有用户信息的列表。
     * @throws BusinessException 如果查询用户信息时发生异常，抛出此异常。
     */
    @Override
    public List<User> getAllUser(){
        try {
            // 查询所有用户
            return userMapper.selectList(null);
        } catch (Exception e) {
            // 如果查询过程中出现异常，抛出业务异常
            throw new BusinessException("查询用户信息失败");
        }

    }
    /**
     * 根据条件查询用户信息。
     *
     * @param selectByConditionDto 查询条件对象，包含班级ID、状态、等级和性别等条件。
     * @return 符合条件的用户列表。
     * @throws BusinessException 如果查询用户信息失败，则抛出业务异常。
     */
    @Override
    public List<User> getUserByCondition(SelectByConditionDTO selectByConditionDto){
        // 初始化查询条件包装器
        QueryWrapper<User> wrapper = new QueryWrapper<>(new User());

        // 如果状态不为空，则添加到查询条件中
        if (selectByConditionDto.getStatus()!= null){
            wrapper.eq("status", selectByConditionDto.getStatus());
        }

        // 如果等级不为空，则添加到查询条件中
        if (selectByConditionDto.getLevel() != null){
            wrapper.eq("level", selectByConditionDto.getLevel());
        }

        // 如果性别不为空，则添加到查询条件中
        if (selectByConditionDto.getGender() != null) {
            wrapper.eq("gender", selectByConditionDto.getGender());
        }

        try {
            // 执行查询，返回符合条件的用户列表
            List<User> userList = userMapper.selectList(wrapper);
            return userList;
        }catch (Exception e){
            // 如果查询过程中出现异常，抛出业务异常
            throw new BusinessException("查询用户信息失败");
        }

    }

    /**
     * 根据用户ID删除用户信息。
     *
     * 本方法通过调用UserMapper的.deleteById方法，尝试删除指定ID的用户记录。
     * 如果删除操作失败，即捕获到异常，将抛出一个自定义的业务异常，指示删除用户操作失败。
     * 这样做的目的是为了在调用方捕获此异常时，能够明确知道删除操作未能成功，并可根据需要进行相应处理。
     *
     * @param userId 需要删除的用户的ID。
     * @throws BusinessException 如果删除用户失败，抛出此异常。
     */
    @Override
    @Transactional
    public void deleteById(Long userId) {
        try {
            userMapper.deleteById(userId);
        } catch (Exception e) {
            throw new BusinessException("删除用户失败");
        }
    }




//    @Override
//    public String modifyPassword(Long userId, ModifyPwdByUserDTO modifyPwdByUserDTO){
//        if (modifyPwdByUserDTO.getOldPassword() == null || modifyPwdByUserDTO.getNewPassword() == null){
//            throw new BusinessException("新密码或者旧密码不能为空");
//        }
//        boolean is_matches = SecurityUtils.matchesPassword(modifyPwdByUserDTO.getOldPassword(), SecurityUtils.getLoginUser().getUser().getPassword());
//        if (!is_matches){
//            throw new BusinessException("旧密码错误");
//        }
//        try {
//            UpdateWrapper<User> wrapper = new UpdateWrapper<>();
//            wrapper.set("password", SecurityUtils.encryptPassword(modifyPwdByUserDTO.getNewPassword()));
//            wrapper.eq("user_id", SecurityUtils.getLoginUser().getUser().getUserId());
//            userMapper.update(null, wrapper);
////            String token = tokenService.createToken(SecurityUtils.getLoginUser());
//            // 用户认证过程
//            Authentication authentication = null;
//            try {
//                // 构建认证令牌
//                UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(SecurityUtils.getLoginUser().getUsername(), modifyPwdByUserDTO.getNewPassword());
//                // 设置认证令牌到线程本地变量
//                ThreadLocalUtil.setContext(authenticationToken);
//                // 执行认证
//                authentication = authenticationManager.authenticate(authenticationToken);
//            } catch (Exception e) {
//                // 处理认证异常
//                if (e instanceof BadCredentialsException) {
//                    // 密码匹配异常
//                    throw new UserPasswordNotMatchException();
//                } else {
//                    // 其他异常
//                    throw new ServiceException(e.getMessage());
//                }
//            } finally {
//                // 清理线程本地变量
//                ThreadLocalUtil.clearContext();
//            }
//            // 获取登录用户信息，并构造令牌所需的声明
//            LoginUser loginUser = (LoginUser) authentication.getPrincipal();
//
//            // 生成并返回令牌
//            tokenService.delLoginUser(loginUser.getToken());
//            return tokenService.createToken(loginUser);
//        }catch (Exception e){
//            throw new BusinessException("修改密码失败");
//        }
//
//    }
//
//    /**
//     * 根据用户ID选择对应的课程名称列表。
//     *
//     * @param userId 用户的ID，用于查询用户信息。
//     * @return 如果用户是班主任，则返回null；否则返回用户所教授的课程名称列表。
//     *
//     * 此方法首先根据用户ID查询用户信息，然后根据用户所在的班级ID判断用户是否是班主任。
//     * 如果用户是班主任，则不返回任何课程名称；如果用户是科任老师，则查询并返回用户教授的课程名称列表。
//     */
//    @Override
//    public List<String> selectCourseNameByUserId(Long userId){
//        // 创建查询包装器，用于查询用户信息
//        QueryWrapper<User> wrapper = new QueryWrapper<>();
//        // 根据用户ID设置查询条件
//        wrapper.eq("user_id", userId);
//        // 根据查询条件查询并获取用户信息
//        User user = userMapper.selectOne(wrapper);
//        // 获取用户的班级ID
//        Long classId = user.getClassId();
//        // 判断用户是否是班主任，如果是则返回null
//        if (classId != null){
//            //如果是班主任
//            return null;
//        }else {
//            // 如果用户不是班主任，查询并返回用户所教授的课程名称列表
//            //如果是科任老师,返回执教科目列表
//            List<String> courseList = courseMapper.selectCourseNameByUserId(userId);
//            return courseList;
//        }
//    }
//    /**
//     * 根据课程ID或班级ID查询选课学生列表。
//     * 此方法用于教师查看其所授课程或班级的选课学生名单。
//     * 如果提供了课程ID(认定为任课教师)，则查询该课程的选课学生；
//     * 如果未提供课程ID(认定为班主任)，则查询学生所属班级的选课学生。
//     *
//     * @param courseId 课程ID，可为null。如果提供，将根据课程查询学生。
//     * @param userId 教师的用户ID，用于确定教师的课程或班级。
//     * @return 返回选课学生的列表，这些学生要么选了指定的课程，要么属于指定教师的班级。
//     */
//    @Override
//    public List<SelectStuByTeacherPO> selectStuList(Long courseId, Long userId) {
//        // 如果提供了课程ID，则根据课程ID查询选课学生
//        if (courseId != null) {
//            List<SelectStuByTeacherPO> users = userMapper.selectByCourseId(courseId, userId);
//            return users;
//        } else {
//            // 如果未提供课程ID，说明是班主任，查询教师所属班级的ID
//            Long classId = userMapper.selectById(userId).getClassId();
//            // 根据班级ID查询该班学生
//            List<SelectStuByTeacherPO> users = userMapper.selectByClassId(classId, userId);
//            return users;
//        }
//    }
//
//
//    /**
//     * 根据学生ID查询学生的选课信息。
//     * <p>
//     * 本方法通过调用courseMapper的selectCourseByUserId方法，根据学生ID获取该学生选修的课程详情。
//     * 这里的学生ID是唯一标识一个学生的关键字段，选课信息包括课程的详细数据。
//     * </p>
//     * @param userId 学生的唯一标识ID。作为查询选课信息的依据。
//     * @return 返回一个SelectCourseByStuPO对象的列表，包含该学生选修的所有课程信息。
//     */
//    public List<SelectCourseByStuPO> selectStuCourse(Long userId){
//        // 调用courseMapper的selectCourseByUserId方法，查询并返回学生选课详情
//        // 调用courseMapper的selectCourseNameByUserId方法，查询并返回用户选课结果
//        return courseMapper.selectCourseByUserId(userId);
//    }
//
//    /**
//     * 查询所有课程信息
//     *
//     * 本方法通过调用courseMapper的selectList方法，返回所有课程的数据列表。
//     * 这里的null参数表示不使用特定的条件查询，即查询所有课程。
//     *
//     * @return List<Course> 返回一个课程列表，包含所有课程的信息。
//     */
//    @Override
//    public List<Course> selectAllCourseInMongo(){
//        return courseMapper.selectList(null);
//    }


    @Override
    public String getAiStudyStyle(String aiStudyStyle) {
//        SystemMessage message = new SystemMessage(ProduceUserDetailsPrompts.STU_STYLE_SYSTEM_PROMPT);
//        HistoriesPrompt historiesPrompt = new HistoriesPrompt();
//        historiesPrompt.addMessage(message);
//        HumanMessage humanMessage = new HumanMessage(aiStudyStyle);
//        historiesPrompt.addMessage(humanMessage);
//        MessageResponse<AiMessage> chat = null;
//        try {
//            chat = sparkLlm.chat(historiesPrompt);
//        } catch (Exception e) {
//            throw new BusinessException(e.getMessage());
//        }
//
//        return chat.getMessage().getContent();
        return null;
    }


    @Override
    public ResultUtil sign(Integer signReward) {
            // 1.获取当前登录用户
            Long userId = SecurityUtils.getUserId();
//            Long userId = 1L;
            // 2.获取日期
            LocalDateTime now = LocalDateTime.now();
            // 3.拼接key
            String keySuffix = now.format(DateTimeFormatter.ofPattern(":yyyyMM"));
            String key = CacheConstants.USER_SIGN_KEY + userId + keySuffix;
            // 4.获取今天是本月的第几天
            int dayOfMonth = now.getDayOfMonth();
            // 5.写入Redis SETBIT key offset 1
            redisCache.setCacheBit(key, dayOfMonth-1, true);
            //6.增加mysql中用户权限时长
            User user = userMapper.selectById(userId);
            Integer newVipTime =  user.getVipTime() + signReward;
            user.setVipTime(newVipTime);
            userMapper.updateById(user);
            return ResultUtil.success("签到成功");
    }

    @Override
    public ResultUtil getSignRewardView() {
        // 1.获取当前登录用户
            Long userId = SecurityUtils.getUserId();
//        Long userId = 1L;
        // 2.获取日期
        LocalDateTime now = LocalDateTime.now();
        // 3.拼接key
        String keySuffix = now.format(DateTimeFormatter.ofPattern(":yyyyMM"));
        String key = CacheConstants.USER_SIGN_KEY + userId + keySuffix;
        // 4.获取今天是本月的第几天
        int dayOfMonth = now.getDayOfMonth();
        // 5..获取本月截止今天之前的所有的签到记录，返回的是一个十进制的数字 BITFIELD sign:5:202203 GET u14 0
        List<Long> result = redisCache.getCacheBitField(key,dayOfMonth-1,0);
        Long num = result.get(0);
        // 6.循环遍历
        int count = 0;
        while (true) {
            // 6.1.让这个数字与1做与运算，得到数字的最后一个bit位  // 判断这个bit位是否为0
            if ((num & 1) == 0) {
                // 如果为0，说明未签到，结束
                break;
            }else {
                // 如果不为0，说明已签到，计数器+1
                count++;
            }
            // 把数字右移一位，抛弃最后一个bit位，继续下一个bit位
            num >>>= 1;
        }

        //8.时长奖励展示设置(分钟)
        List<Integer> rewards = new ArrayList<>();
        if(count < 15){
            for (int i = 0; i <7 ; i++) {
                rewards.add(i,30+10*(count+i));
            }
        }else {
            for (int i = 0; i < 7; i++) {
                rewards.add(180);
            }
        }

        //   7..判断用户今日是否签到，如果已经签到直接传回已签到的信息
        if (redisCache.getCacheBit(key,dayOfMonth-1)){
            int signCount = count + 1;
            HaveSignVO haveSignVO = new HaveSignVO(signCount,rewards.get(0));
            return ResultUtil.error("今日已签到，已连续签到"+signCount+"天",haveSignVO);
        }
        SignRewardVO rewardVO = new SignRewardVO();
        rewardVO.setRewards(rewards);
        rewardVO.setSignCount(count);
        return ResultUtil.success(rewardVO);
    }

    @Override
    public Boolean registerByOpenId(String username, String password, String openid) {
            //判断该该微信openId是否已注册用户
            if(userMapper.selectOne(new LambdaQueryWrapper<User>().eq(User::getOpenId, openid)) != null){
                throw new BusinessException("你的微信账号已经绑定了一个用户");
            }
            // 根据用户名查询用户信息
            LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
            wrapper.eq(User::getUserName, username);
            User user = userMapper.selectOne(wrapper);
            // 判断用户名是否存在
            if(!(Objects.isNull(user))){
                throw new BusinessException("用户名存在");
            }
            user = new User();
            // 使用BCryptPasswordEncoder对密码进行加密
            BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
            user.setPassword(encoder.encode(password));
            user.setUserName(username);
            user.setOpenId(openid);
            user.setAvatarUrl("https://jinghong.oss-cn-hangzhou.aliyuncs.com/image/2025-05-13/62f5a41b-c15b-4a72-871f-574dbf315523.jpg");
            userMapper.insert(user);
            return true;
    }

    @Override
    public Boolean resetPassword(String password) {
        try {
            Long userId = SecurityUtils.getUserId();
            BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
            String newPassword = encoder.encode(password);
            userMapper.update(null, new LambdaUpdateWrapper<User>().eq(User::getUserId, userId).set(User::getPassword, newPassword));
            return true;
        }catch (Exception e) {
            e.printStackTrace();
            throw new BusinessException("重置密码失败");
        }
    }


    public ResultUtil getSignMothView() {
        // 1.获取当前登录用户
        Long userId = SecurityUtils.getUserId();
        // 2.获取日期
        LocalDateTime now = LocalDateTime.now();
        // 3.拼接key
        String keySuffix = now.format(DateTimeFormatter.ofPattern(":yyyyMM"));
        String key = CacheConstants.USER_SIGN_KEY + userId + keySuffix;
        // 4.获取传入月份有多少天
        YearMonth yearMonth = YearMonth.from(now);
        // 5.获取当前月的天数
        int days= yearMonth.lengthOfMonth();
        // 6.获取位图的无符号十进制整数
        List<Long> list = redisCache.getCacheBitField(key,days,0);
        if (list == null || list.isEmpty()) {
            return null;
        }
        // 7.获取位图的无符号十进制整数值
        long bitMapNum = list.get(0);
        // 8.进行位运算判断组装那些日期有签到
        List<Map> result = new ArrayList<>();
        for (int i = days; i > 0; i--) {
            Map<String, Object> map = new HashMap<>();
            map.put("day", i);
            //先 右移，然后在 左移，如果得到的结果仍然与本身相等，则 最低位是0 所以是未签到
            if (bitMapNum >> 1 << 1 == bitMapNum) {
                map.put("active", false);
            } else {
                //与本身不等，则最低位是1 表示已签到
                map.put("active", true);
            }
            result.add(map);
            // 将位图的无符号十进制整数右移一位，准备下一轮判断
            bitMapNum >>= 1;
        }
        Collections.reverse(result);
        return ResultUtil.success(result);
    }
}




